package cn.sc.summer.mybatis.runner.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.sc.summer.constant.gateway.GatewayConstant;
import cn.sc.summer.constant.project.ProjectConstant;
import cn.sc.summer.constant.util.AssertUtil;
import cn.sc.summer.constant.util.MatchUtil;
import cn.sc.summer.exception.exceptions.CommonException;
import cn.sc.summer.mybatis.mapper.SysApiMapper;
import cn.sc.summer.mybatis.po.SysApi;
import cn.sc.summer.mybatis.runner.service.ApiService;
import cn.sc.summer.redis.util.RedisHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 类名：api扫描实现层
 *
 * @author a-xin
 * @date 2024/1/29 09:24
 */
@Slf4j
@Service
public class ApiServiceImpl implements ApiService {

    @Resource
    private WebApplicationContext webApplicationContext;

    @Resource
    private SysApiMapper sysApiMapper;

    /**
     * 扫描接口权限信息
     */
    @Override
    public void apiScan() {
        log.info("==> {} : Starts caching all interface information......", ProjectConstant.SERVER_NAME);
        String routing = checkRoute();
        RequestMappingHandlerMapping mapping = webApplicationContext.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class);
        // 获取url与类和方法的对应信息
        Map<RequestMappingInfo, HandlerMethod> methodMap = mapping.getHandlerMethods();
        List<SysApi> sysApis = new ArrayList<>();
        for (Map.Entry<RequestMappingInfo, HandlerMethod> methodEntry : methodMap.entrySet()) {

            HandlerMethod method = methodEntry.getValue();
            String className = method.getMethod().getDeclaringClass().getName();
            if (MatchUtil.matchPattern(ProjectConstant.BASE_PACKAGE, className)) {

                SysApi sysApi = new SysApi();
                RequestMappingInfo info = methodEntry.getKey();

                PatternsRequestCondition patternsCondition = info.getPatternsCondition();
                String url = (String) patternsCondition.getPatterns().stream().toArray()[0];
                String rawPath = routing.replace("/**", url);

                sysApi.setServer(ProjectConstant.SERVER_NAME);
                sysApi.setApi(rawPath);
                sysApi.setApiDesc(rawPath);

                RequestMethodsRequestCondition methodsCondition = info.getMethodsCondition();
                String requestType = methodsCondition.getMethods().stream().map(String::valueOf)
                        .collect(Collectors.joining(","));

                sysApi.setMethod(CharSequenceUtil.isBlank(requestType) ? HttpMethod.GET.name() : requestType);
                sysApis.add(sysApi);
            }
        }
        updateApiInfo(sysApis);
    }

    /**
     * 更新全局api信息
     *
     * @param sysApis api信息
     */
    private void updateApiInfo(List<SysApi> sysApis) {
        if (CollUtil.isNotEmpty(sysApis)) {
            List<SysApi> sysApiList = sysApiMapper.selectList(SysApi::getServer, ProjectConstant.SERVER_NAME);
            if (CollUtil.isNotEmpty(sysApiList)) {
                Map<String, SysApi> stringSysApiMap = sysApis.stream()
                        .collect(Collectors.toMap(SysApi::getApi, r -> r));

                sysApis.clear();

                Map<String, SysApi> apiMap = new HashMap<>();
                sysApiList.stream().forEach(sysApi -> {
                    apiMap.put(sysApi.getApi(), sysApi);
                    if (stringSysApiMap.containsKey(sysApi.getApi())) {
                        SysApi sysApi1 = stringSysApiMap.get(sysApi.getApi());
                        BeanUtils.copyProperties(sysApi1, sysApi, "id");
                        sysApiMapper.updateById(sysApi);
                        stringSysApiMap.remove(sysApi.getApi());
                    }
                });

                List<String> deleteIds = new ArrayList<>();
                if (CollUtil.isNotEmpty(stringSysApiMap)) {
                    stringSysApiMap.forEach((key, value) -> {
                        sysApis.add(value);
                        if (!apiMap.containsKey(key)) {
                            deleteIds.add(value.getId());
                        }
                    });
                    sysApiMapper.insertBatchSomeColumn(sysApis);
                }
                AssertUtil.functionIfPre(deleteIds, CollUtil::isNotEmpty, () -> {
                    sysApiMapper.deleteBatchIds(deleteIds);
                    sysApiMapper.deleteApiRoute(deleteIds);
                });
            } else {
                sysApiMapper.insertBatchSomeColumn(sysApis);
            }
        }
    }

    /**
     * 校验网关路由是否注册
     *
     * @return 路由前缀
     */
    private String checkRoute() {
        int num = 0;
        String routing = (String) RedisHelper.hGet(GatewayConstant.ROUTING_PREFIX, ProjectConstant.SERVER_NAME);
        while (!RedisHelper.hasKey(GatewayConstant.ROUTING_PREFIX) || CharSequenceUtil.isBlank(routing)) {
            log.error("==> Please start the gateway service first!");
            try {
                Thread.sleep(5000L);
                num++;
            } catch (InterruptedException e) {
                throw new CommonException(e.getMessage());
            }
            if (num > 10) {
                Thread.currentThread().interrupt();
                throw new CommonException("Please start the gateway service first!");
            }
        }
        return routing;
    }

}
